﻿/* dk2Repository - Copyright (C) 2011 Daniel Kuczewski http://www.dk2.de/

 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using dk2Database.Commands;
using dk2Repository.Interfaces;

namespace dk2Repository.Commands
{
    public class MappedQueryCommand<T> : QueryEntityCommandBase<IList<T>> where T : class, new()
    {

        private IMapping _mapping;
        private List<References.Reference> _usedReferences;

        public MappedQueryCommand(string sql) : base(sql) {Init();}
        public MappedQueryCommand(string sql, CommandParameters parameters) : base(sql, parameters) { Init(); }
        public MappedQueryCommand(string sql, params object[] parameters) : base(sql, parameters) { Init(); }

        private void Init()
        {
            _mapping = MappingFactory.GetMappingInfo<T>();
            _usedReferences = new List<References.Reference>();
        }


        public override IList<T> ProcessDataReader(IDataReader reader)
        {
            var list = new List<T>();
            var fields = reader.FieldCount;

            var setters = new IValueSetter[fields];

            for (var i = 0; i < fields; i++)
                setters[i] = GetValueSetter(reader.GetName(i));

            var references = _usedReferences.ToArray();

            while (reader.Read())
            {
                var values = new object[fields];
                reader.GetValues(values);

                var item = (T)_mapping.CreateInstance();

                for (var i = 0; i < fields; i++)
                    setters[i].SetDbValue(item, values[i]);

                item = ProcessItem(values, item);

                if (item is IResetable)
                    ((IResetable)item).Reset();

                foreach (var reference in references)
                {
                    var refitem = reference.GetReferenceItem(item);
                    if (refitem != null && refitem is IResetable)
                        ((IResetable)refitem).Reset();

                }

                list.Add(item);
            }


            return list;
        }

        public virtual T ProcessItem(object[] values, T item)
        {
            return item;
        }

        private IValueSetter GetValueSetter(string field)
        {
            IValueSetter vs = null;
            var reference = _mapping.References.FirstOrDefault(x =>
                                                                        field.StartsWith(x.Prefix) &&
                                                                        field.Length > x.Prefix.Length
                                                                   );

            if (reference != null)
            {
                field = field.Substring(reference.Prefix.Length);

                _usedReferences.Add(reference);
                if (reference.ReferenceMapping.Columns.Contains(field))
                    vs = new ValueSetters(reference, reference.ReferenceMapping.Columns[field]);

            }
            else
            {
                if (_mapping.Columns.Contains(field))
                    vs = new ColumnSetter(_mapping.Columns[field]);
            }

            return vs ?? (new NullSetter());
        }



    }
}
